#include "protocol.h"
#include "device.h"

uint16_t reportPacketID;

ParamTable gParamTable = {0};
extern device_info_t gDevice;

void registry_param(uint16_t id, void *value, uint8_t length)
{
    if (gParamTable.tableIndex >= Count_ParamItem_Max) {
        return;
    }
    gParamTable.table[gParamTable.tableIndex].id = id;
    gParamTable.table[gParamTable.tableIndex].value = value;
    gParamTable.table[gParamTable.tableIndex].length = length;
    gParamTable.tableIndex++;
}

void set_param_flag(void *value)
{
    uint16_t i;

    for (i = 0; i < gParamTable.tableIndex; i++) {
        if (gParamTable.table[i].value != NULL) {
            if (gParamTable.table[i].value == value) {
                gParamTable.table[i].flag = 1;
                break;
            }
        }
    }
}

bool is_param_flag(uint16_t id)
{
    uint16_t i;

    for (i = 0; i < gParamTable.tableIndex; i++) {
        if (gParamTable.table[i].id == id) {
            if (gParamTable.table[i].flag == 1) {
                return true;
            }
        }
    }
    return false;
}

uint16_t getCRCExt(uint16_t crc, void *buffer, uint16_t length)
{
    uint8_t i;

    uint8_t *p = (uint8_t *)buffer;

    while (length-- != 0) {
        for (i = 0x80; i != 0; i /= 2) {
            if ((crc & 0x8000) != 0) {
                crc *= 2;
                crc ^= 0x1021;
            }
            else {
                crc *= 2;
            }
            if ((*p & i) != 0) {
                crc ^= 0x1021;
            }
        }
        p++;
    }
    return crc;
}

uint16_t getCRC(void *buffer, uint16_t length)
{
    return getCRCExt(0, buffer, length);
}

#define Transcode_Buffer_Size Count_ProtocolData_Max * 6 / 5
static int8_t tempTransBuffer[Transcode_Buffer_Size];

uint16_t addTransChar(uint8_t *inBuffer, uint32_t length, uint8_t *outBuffer, uint32_t size, uint8_t header, uint8_t tailer, uint8_t transChar)
{
    int32_t i, index = 0;

    for (i = 0; i < length; i++) {
        if ((inBuffer[i] == header) || (inBuffer[i] == tailer) || (inBuffer[i] == transChar)) {
            tempTransBuffer[index++] = transChar;
            tempTransBuffer[index++] = inBuffer[i] - 1;
        }
        else {
            tempTransBuffer[index++] = inBuffer[i];
        }
    }
    if (index > size) {
        return 0;
    }
    memcpy(outBuffer, tempTransBuffer, index);

    return index;
}

uint16_t delTransChar(uint8_t *inBuffer, uint32_t length, uint8_t *outBuffer, uint32_t size, uint8_t transChar)
{
    int32_t i, index = 0;

    for (i = 0; i < length; i++) {
        if (inBuffer[i] == transChar) {
            i++;
            tempTransBuffer[index++] = inBuffer[i] + 1;
        }
        else {
            tempTransBuffer[index++] = inBuffer[i];
        }
    }
    if (index > size) {
        return 0;
    }
    memcpy(outBuffer, tempTransBuffer, index);

    return index;
}

uint8_t combineByte(uint8_t b1, uint8_t b2)
{
    if (b1 >= 0x30 && b1 <= 0x39) {
        b1 -= 0x30;
    }
    else {
        b1 -= 0x37;
    }

    if (b2 >= 0x30 && b2 <= 0x39) {
        b2 -= 0x30;
    }
    else {
        b2 -= 0x37;
    }

    return (b1 << 4) + b2;
}

uint16_t combine(uint8_t *inBuffer, uint32_t length, uint8_t *outBuffer, uint32_t size)
{
    int32_t i = 0;

    if ((length % 2 != 0) || (size < length / 2)) {
        return 0;
    }

    for (i = 0; i < length / 2; i++) {
        tempTransBuffer[i] = combineByte((uint8_t)inBuffer[i * 2], (uint8_t)inBuffer[i * 2 + 1]);
    }

    memcpy(outBuffer, tempTransBuffer, length / 2);

    return length / 2;
}

uint16_t splitByte(uint8_t b)
{
    uint8_t b1, b2;
    b1 = b >> 4;
    b2 = b & 0x0F;

    if (b1 <= 9) {
        b1 += 0x30;
    }
    else {
        b1 += 0x37;
    }

    if (b2 <= 9) {
        b2 += 0x30;
    }
    else {
        b2 += 0x37;
    }

    return (b1 << 8) + b2;
}

uint16_t split(uint8_t *inBuffer, uint32_t length, uint8_t *outBuffer, uint32_t size)
{
    uint32_t i = 0;
    uint16_t w = 0;

    if (size < length * 2) {
        return 0;
    }

    for (i = 0; i < length; i++) {
        w = splitByte(inBuffer[i]);
        tempTransBuffer[i * 2] = w >> 8;
        tempTransBuffer[i * 2 + 1] = w;
    }

    memcpy(outBuffer, tempTransBuffer, length * 2);

    return 2 * length;
}

uint8_t parseProtocolInter(uint8_t apType, SProtocolData *pData, uint8_t *buffer, uint32_t count, uint8_t isCheckCRC)
{
    uint32_t i;
    uint16_t length;

    // AP协议再次鉴权和数据还原，其实parseProtocol已经鉴权，可以省略default
    switch (apType) {
        case ProtocolType_Protocol_APA:
        case ProtocolType_Protocol_APC:
            length = delTransChar(buffer, count, buffer, count, Transcode_Protocol_APA);
            break;
        case ProtocolType_Protocol_APB:
            length = combine(buffer, count, buffer, count);
            break;
        default:
            return Answer_Protocol_Initiative;
    }

    // 还原后数据小于最小数据长度
    if (length < Length_Protocol_Min) {
        return Answer_Protocol_Initiative;
    }

    memcpy(pData, buffer, length - Length_Protocol_Checksum);
    memcpy((void *)&pData->checksum, &buffer[length - Length_Protocol_Checksum], Length_Protocol_Checksum);
    pData->count = length - Length_Protocol_Min;

    // 仅仅APB协议才鉴权是否相等，可以避免本地采用APC协议导致数据被丢掉
    if (apType == ProtocolType_Protocol_APB) {
        if (apType != pData->apType) {
            return Answer_Protocol_Initiative;
        }
    }
    else if ((pData->apType == ProtocolType_Protocol_APA) || (pData->apType == ProtocolType_Protocol_APC)) {
        if ((pData->apType != ProtocolType_Protocol_APA) && (pData->apType != ProtocolType_Protocol_APC)) {
            return Answer_Protocol_Initiative;
        }
    }
    else {
        return Answer_Protocol_Initiative;
    }

    // 校验和鉴权
    if (isCheckCRC && (pData->checksum != getCRC((void *)pData, length - Length_Protocol_Checksum))) {
        return Answer_Protocol_Initiative;
    }

    // VP层协议鉴权
    if (pData->vpType != ProtocolType_Protocol_VPA) {
        return Answer_Protocol_Initiative;
    }

    // MCP和命令编号鉴权
    switch (pData->mcpType) {
        case ProtocolType_Protocol_MCPA:
            if ((pData->cmdID != CmdID_Protocol_Alarm) && (pData->cmdID != CmdID_Protocol_Query) &&
                (pData->cmdID != CmdID_Protocol_Set) && (pData->cmdID != CmdID_Protocol_Update) &&
                (pData->cmdID != CmdID_Protocol_Version))  // zlh
            {
                return Answer_Protocol_CmdIDError;
            }
            break;
        case ProtocolType_Protocol_MCPB:
            if ((pData->cmdID != CmdID_Protocol_Read) && (pData->cmdID != CmdID_Protocol_Write)) {
                return Answer_Protocol_CmdIDError;
            }
            break;
        default:
            return Answer_Protocol_Initiative;
    }

    // 长度鉴权(为了从功能上区别，故意和上面代码独立分开写)
    switch (pData->mcpType) {
        case ProtocolType_Protocol_MCPA:
            for (i = 0; i < pData->count;) {
                // 是否小于最小数据长度
                if (((SMcpDataA *)&pData->datas[i])->length < sizeof(SMcpDataA)) {
                    return Answer_Protocol_LengthError;
                }
                i += ((SMcpDataA *)&pData->datas[i])->length;
            }
            break;
        default:
            return Answer_Protocol_Initiative;
    }

    if (i != pData->count) {
        return Answer_Protocol_LengthError;
    }

    return Answer_Protocol_Success;
}

/**
 * 标准协议的数据解包分析函数。返回0xFF表示解析失败，需要丢弃处理，返回其它值参见标准协议的应答标志规定。
 */
uint8_t parseProtocol(uint8_t apType, SProtocolData *pData, uint8_t *buffer, uint32_t count, uint8_t isCheckCRC, uint32_t *end)
{
    int32_t i;
    uint8_t prefix, postfix;
    uint16_t start = 0, stop;
    uint8_t parseResult;

    switch (apType) {
        case ProtocolType_Protocol_APA:
        case ProtocolType_Protocol_APC:
            prefix = Prefix_Protocol_APA;
            postfix = Postfix_Protocol_APA;
            break;
        default:
            return Answer_Protocol_Initiative;
    }

    while (1) {
        // to find prefix
        for (i = start; i < count; i++) {
            if (buffer[i] == prefix) {
                start = i;
                stop = start + 1;
                break;
            }
        }
        if (i == count) {
            // can not find prefix
            return Answer_Protocol_Initiative;
        }

        // to find postfix
        for (i = stop; i < count; i++) {
            if (buffer[i] == postfix) {
                stop = i;
                break;
            }
        }
        if (i == count) {
            return Answer_Protocol_Initiative;
        }

        // 注意这里传递数据进去时，已经去掉了包头和包尾
        parseResult = parseProtocolInter(apType, pData, &buffer[start + 1], stop - start - 1, isCheckCRC);
        if (parseResult != Answer_Protocol_Initiative) {
            if (end != NULL) {
                *end = stop;
            }
            return parseResult;
        }
        start++;
    }
}

uint32_t packetProtocol(SProtocolData *pData, uint8_t *buffer, uint32_t size)
{
    uint32_t length;

    if (size < pData->count + Length_Protocol_Min + 2) {
        return 0;
    }

    pData->checksum = getCRC((uint8_t *)pData, pData->count + Length_Protocol_Head);

    memcpy(&buffer[1], (void *)pData, pData->count + Length_Protocol_Head);
    memcpy(&buffer[pData->count + Length_Protocol_Head + 1], (void *)&pData->checksum, Length_Protocol_Checksum);

    if ((pData->apType == ProtocolType_Protocol_APA) || (pData->apType == ProtocolType_Protocol_APC)) {
        length = addTransChar(&buffer[1], pData->count + Length_Protocol_Min, &buffer[1], size - 2,
                              Prefix_Protocol_APA, Postfix_Protocol_APA, Transcode_Protocol_APA);

        buffer[0] = Prefix_Protocol_APA;
        buffer[length + 1] = Postfix_Protocol_APA;

        return (length + 2);
    }

    return 0;
}

uint16_t getPacketID()
{
    static uint16_t packetID = 0;

    packetID++;

    if ((packetID < PacketID_Alarm_Min) || (packetID >= PacketID_Report_Min))
        packetID = PacketID_Alarm_Min;

    return packetID;
}

uint16_t getReportPacketID(void)
{
    reportPacketID++;
    if ((reportPacketID < PacketID_Report_Min) || (reportPacketID >= PacketID_OMT_Report_Min)) {
        reportPacketID = PacketID_Report_Min;
    }

    return reportPacketID;
}

uint16_t cmdProtocol(uint8_t apType, uint8_t cmdID, uint16_t packetID, uint32_t majorNumber, uint8_t minorNumber, uint8_t mcpType, uint8_t *datas, uint32_t length, uint32_t size)
{
    SProtocolData sData;
    sData.apType = apType;
    sData.vpType = ProtocolType_Protocol_VPA;
    sData.majorNumber = majorNumber;
    sData.minorNumber = minorNumber;
    sData.packetID = packetID;
    sData.vpFlag = VPFlag_Protocol_Initiative;
    sData.mcpType = mcpType;
    sData.cmdID = cmdID;
    sData.answer = Answer_Protocol_Initiative;
    sData.count = length;

    memcpy(sData.datas, datas, length);

    return packetProtocol(&sData, datas, size);
}

SMcpDataA *parseMcpDataA(uint8_t *buffer, uint32_t length, uint32_t *offset)
{
    uint8_t *pointer;

    if (((*offset) + 1) >= length) {
        return NULL;
    }
    else {
        pointer = buffer + *offset;
        (*offset) += ((SMcpDataA *)pointer)->length;

        return (SMcpDataA *)pointer;
    }
}

void paramProcessorInitial(void)
{
    memset(&gParamTable, 0, sizeof(gParamTable));
}

uint16_t getParamIndex(uint16_t id)
{
    uint16_t i;

    for (i = 0; i < Count_ParamItem_Max; i++) {
        if (id == gParamTable.table[i].id) {
            return i;
        }
    }
    return Count_ParamItem_Max;
}

void update_param_value(uint16_t id, uint8_t *value, uint8_t length)
{
    uint16_t index;

    index = getParamIndex(id);
    if (index >= Count_ParamItem_Max) {
        return;
    }
    if (gParamTable.table[index].length != length) {
        return;
    }
    memcpy(gParamTable.table[index].value, value, length);
}

uint8_t procesParamTable(uint8_t commOrigin, uint8_t cmdID, uint8_t *buffer, uint16_t length)
{
    uint32_t offset;
    int8_t result;
    SMcpDataA *pData;
    uint16_t index;

    result = Answer_Protocol_Success;
    offset = 0;
    pData = parseMcpDataA(buffer, length, &offset);

    while (pData != NULL) {
        // 查询参数列表
        index = getParamIndex(pData->id);
        // 参数编号过大
        if (index >= Count_ParamItem_Max) {
            pData->id |= ParamFlag_Protocol_Unknown;
            result = Answer_Protocol_ConditionExecution;
        }
        else if (gParamTable.table[index].value == NULL) {
            pData->id |= ParamFlag_Protocol_Unknown;
            result = Answer_Protocol_ConditionExecution;
        }
        else if (gParamTable.table[index].length != pData->length - 3) {
            pData->id |= ParamFlag_Protocol_LengthError;
            result = Answer_Protocol_ConditionExecution;
        }
        else {
            update_param_value(pData->id, pData->datas, pData->length - 3);
        }
        pData = parseMcpDataA(buffer, length, &offset);
    }
    return result;
}

uint32_t packetAnswer(uint8_t commOrigin, SProtocolData *pData, uint8_t *buffer, uint32_t size)
{
    return packetProtocol(pData, buffer, size);
}

uint32_t dataHandler(uint8_t commOrigin, uint8_t *buffer, uint32_t count, uint32_t size, uint32_t *end)
{
    uint8_t parseResult;
    SProtocolData protocolData;

    parseResult = parseProtocol(ProtocolType_Protocol_APA, &protocolData, buffer, count, 1, end);
    if (parseResult == Answer_Protocol_Initiative) {
        return 0;
    }

    if (protocolData.cmdID == CmdID_Protocol_Query) {
        procesParamTable(commOrigin, protocolData.cmdID, protocolData.datas, protocolData.count);
    }

    return 0;
}
